本系列文已改編成書「甚麼?網頁也可以做派對遊戲?使用 Vue 和 babylon.js 打造 3D 派對遊戲吧!」
書中不只重構了程式架構、改善了介面設計,還新增了 2 個新遊戲呦!ˋ( ° ▽、° )
新遊戲分別使用了陀螺儀與震動回饋,趕快買書來研究研究吧!ლ(╹∀╹ლ)
在此感謝深智數位的協助,歡迎大家前往購書,鱈魚感謝大家 (。・∀・)。
助教:「所以到底差在哪啊?沒圖沒真相,被你坑了都不知道。(´。_。`)」
鱈魚:「你對我是不是有甚麼很深的偏見啊 (っ °Д °;)っ,來人啊,上連結!」
一片空白的遊戲大廳,真是讓人覺得開了個寂寞派對,讓我們來打造一個背景吧!( •̀ ω •́ )✧
新增 background-polygons-pattern 組件。
src\components\background-polygons-pattern.vue
<template>
</template>
<script setup lang="ts">
import { ref } from 'vue';
interface Props {
label?: string;
}
const props = withDefaults(defineProps<Props>(), {
label: '',
});
</script>
<style scoped lang="sass">
</style>
把 background-polygons-pattern 做為背景,加到 game-console-lobby 中。
src\views\game-console-lobby.vue
<template>
<background-polygons-pattern class="absolute inset-0" />
</template>
<script setup lang="ts">
import BackgroundPolygonsPattern from '../components/background-polygons-pattern.vue';
import { useLoading } from '../composables/use-loading';
const loading = useLoading();
loading.hide();
</script>
<style scoped lang="sass">
</style>
接著先定義一下 Props。
interface Props {
mainColor?: string;
}
const props = withDefaults(defineProps<Props>(), {
mainColor: '#b3e6d0',
});
接著一樣是新增 backgroundStyle,產生背景顏色。
const backgroundStyle = computed(() => {
// 變亮
const lightenColor = lighten(props.mainColor, 10);
// 變暗並偏移色相
const darkColor = lighten(props.mainColor, -10);
const hsvColor = rgbToHsv(textToRgb(darkColor));
hsvColor.h -= 20;
const offsetColor = rgbToHex(hsvToRgb(hsvColor));
return {
background: `linear-gradient(-30deg, ${offsetColor}, ${props.mainColor}, ${lightenColor}, ${props.mainColor}, ${offsetColor})`
}
});
並在 template 加入最外層 div 並綁定 style。
<template>
<div
class="overflow-hidden"
:style="backgroundStyle"
>
</div>
</template>
...
<script setup lang="ts">
import { computed } from 'vue';
import { colors } from 'quasar';
const { lighten, textToRgb, rgbToHsv, hsvToRgb, rgbToHex } = colors;
...
const backgroundStyle = computed(() => { ... });
</script>
...
顏色出現了,終於不是全白的寂寞派對了!(≧∇≦)ノ
接著準備加入裝飾,預期效果是網點壁紙的感覺,直接拿先前的 polygon-base 組件來用吧。
產生多邊形。
import PolygonBase, { ShapeType } from './polygon-base.vue';
const shapeTypes = Object.values(ShapeType);
const polygons: ShapeType[] = [];
for (let i = 0; i < 500; i++) {
polygons.push(shapeTypes[i % shapeTypes.length]);
}
在模板產生多邊形。
<template>
<div
...
>
<div class="pattern flex w-full h-full gap-10">
<polygon-base
v-for="(shape, i) in polygons"
:key="i"
size="2rem"
fill="solid"
:shape="shape"
color="#f9fff0"
/>
</div>
</div>
</template>
<script setup lang="ts">
...
import PolygonBase, { ShapeType } from './polygon-base.vue';
...
const shapeTypes = Object.values(ShapeType);
const polygons: ShapeType[] = [];
for (let i = 0; i < 500; i++) {
polygons.push(shapeTypes[i % shapeTypes.length]);
}
</script>
有了。( ´◡` )
接著加點動畫效果吧。
<template>
<div
...
>
<div class="pattern flex w-full h-full gap-10">
<polygon-base
v-for="(shape, i) in polygons"
...
class="polygon"
...
/>
</div>
</div>
</template>
...
<style scoped lang="sass">
.pattern
transform: rotate(-6deg) translate(2%, -5%)
.polygon
animation: polygon 3s infinite
@keyframes polygon
0%, 100%
opacity: 0.2
30%, 70%
opacity: 0.4
</style>
仔細看會發現多邊形有波浪舞的感覺。✧*。٩(ˊᗜˋ*)و✧*。
接著讓我們加上主選單、玩家頭像、房間 ID 和遊戲選擇面板吧
建立以下組件:
先加上基本外觀與樣式,再慢慢完成細節。
src\components\room-id-chip.vue
<template>
<div class="room-id py-8 px-16 rounded-full">
{{ roomId }}
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { useGameConsoleStore } from '../stores/game-console.store';
interface Props {
color?: string;
}
const props = withDefaults(defineProps<Props>(), {
color: '#111',
});
const gameConsoleStore = useGameConsoleStore();
const roomId = computed(() => gameConsoleStore.roomId);
</script>
<style scoped lang="sass">
.room-id
background: rgba(white, 0.4)
color: v-bind('props.color')
font-size: 2rem
letter-spacing: 2px
font-weight: 700
</style>
src\components\player-avatar.vue
<template>
<q-avatar
color="white"
text-color="black"
size="6rem"
>
{{ props.codeName }}
</q-avatar>
</template>
<script setup lang="ts">
interface Props {
/** 玩家 ID,同 client ID */
playerId: string;
/** 玩家代號 */
codeName: string;
}
const props = withDefaults(defineProps<Props>(), {});
</script>
<style scoped lang="sass">
</style>
src\components\game-tab-panel.vue
<template>
<div class="panel relative rounded-[3rem]">
</div>
</template>
<script setup lang="ts">
</script>
<style scoped lang="sass">
.panel
width: 84vmin
height: 84vmin
background: rgba(white, 0.8)
overflow: hidden
border: 1rem solid white
</style>
完成 game-console-lobby 基本內容結構。
src\views\game-console-lobby.vue
<template>
<background-polygons-pattern class="absolute inset-0" />
<div class="absolute inset-0 flex">
<div class="flex w-full h-full">
<!-- 選單 -->
<div class="w-1/3 flex flex-col p-12">
<div class="flex flex-col flex-1 justify-center items-center gap-14">
<room-id-chip color="#67785d" />
<btn-base
label="開始遊戲"
class="w-96"
label-hover-color="#7b916e"
stroke-hover-color="white"
/>
<btn-base
label="結束派對"
class="w-96"
label-hover-color="#7b916e"
stroke-hover-color="white"
/>
</div>
<!-- 玩家清單 -->
<transition-group
name="list"
tag="div"
class="flex justify-center items-center gap-4 h-32"
>
<player-avatar
player-id="id"
code-name="1P"
/>
</transition-group>
</div>
<!-- 選擇遊戲 -->
<div class="w-2/3 flex flex-nowrap justify-between items-center flex-1 px-16">
<btn-base label="◀" />
<game-tab-panel />
<btn-base label="▶" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import BackgroundPolygonsPattern from '../components/background-polygons-pattern.vue';
import GameTabPanel from '../components/game-tab-panel.vue';
import PlayerAvatar from '../components/player-avatar.vue';
import RoomIdChip from '../components/room-id-chip.vue';
import BtnBase from '../components/btn-base.vue';
...
</script>
...
現在看起來應該會長這樣。
看起來終於有一點遊戲大廳的樣子了!✧*。٩(ˊᗜˋ*)و✧*。
以上程式碼已同步至 GitLab,大家可以前往下載: